#!/usr/bin/env python
# _*_coding:utf-8_*_

"""
@Time :    2019/9/28 20:00
@Author:  ThinHeader
@File: decorators.py
@Software: PyCharm
"""
import hmac
import json
import logging
import time
from functools import wraps
from urllib.parse import unquote

from celery.local import class_property
from django.conf import settings
from django.contrib.auth import authenticate, login
from django.core import cache
from django.shortcuts import redirect
from rest_framework.authtoken.models import Token

from common.tools.redis_tool import RedisStat
from common.wx.WXBizMsgCrypt import WxCryptConfig
from common.wx.lib import WeixinHelper
from open.models import WxPublicMapShop

logger = logging.getLogger('qixiaobao')


class CommonHelper(object):

    @class_property
    def expire(cls):
        """比真实过期减少时间"""
        return 300

    @class_property
    def cache(cls):
        """返回cache对象"""

    @class_property
    def access_token_key(cls):
        return "ACCESS_TOKEN"

    @class_property
    def jsapi_ticket_key(cls):
        return "JSAPI_TICKET"

    @class_property
    def access_token(cls):
        cache, key = cls.cache, cls.access_token_key
        token = cache.get(key)
        if not token:
            data = json.loads(WeixinHelper.getAccessToken())
            token, expire = data["access_token"], data["expires_in"]
            cache.set(key, token, expire - cls.expire)
        return token

    @class_property
    def jsapi_ticket(cls):
        cache, key = cls.cache, cls.jsapi_ticket_key
        ticket = cache.get(key)
        if not ticket:
            data = json.loads(WeixinHelper.getJsapiTicket(cls.access_token))
            ticket, expire = data["ticket"], data["expires_in"]
            cache.set(key, ticket, expire - cls.expire)
        return ticket

    @classmethod
    def send_text_message(cls, openid, message):
        """客服主动推送消息"""
        return WeixinHelper.sendTextMessage(openid, message, cls.access_token)

    @classmethod
    def jsapi_sign(cls, url):
        """jsapi_ticket 签名"""
        return WeixinHelper.jsapiSign(cls.jsapi_ticket, url)

    @classmethod
    def jsapi_sign_ticket(cls, ticket, url):
        """jsapi_ticket 签名"""
        return WeixinHelper.jsapiSign(ticket, url)

    @classmethod
    def hmac_sign(cls, key):
        return hmac.new(cls.secret_key.encode('utf8'), key.encode('utf8')).hexdigest()

    @classmethod
    def sign_cookie(cls, key):
        """cookie签名"""
        return "{0}|{1}".format(key, cls.hmac_sign(key))

    @classmethod
    def check_cookie(cls, value):
        """
        验证cookie
        成功返回True, key
        """
        code = value.split("|", 1)
        if len(code) != 2:
            return False, None
        key, signature = code
        if cls.hmac_sign(key) != signature:
            return False, None
        return True, key


class Helper(CommonHelper):
    """微信具体逻辑帮助类"""

    @class_property
    def cache(cls):
        """返回cache对象"""
        return cache

    @class_property
    def secret_key(cls):
        """返回cookie加密秘钥"""
        return settings.SECRET_KEY


def sns_userinfo_callback(callback=None):
    """
    网页授权获取用户信息装饰器:访问mobile端时，判断如果未授权重定向到授权view。
    callback(openid, userinfo):
        return user
    """

    def wrap(func):
        @wraps(func)
        def inner(request, *args, **kwargs):
            if 'MicroMessenger' in request.META.get('HTTP_USER_AGENT', ''):
                shop = request.GET.get('shop', None)
                if not shop:
                    """没有shop时认为是测试链接直接渲染返回"""
                    response = func(request, *args, **kwargs)
                else:
                    # 判断访问域名
                    if request.META['HTTP_HOST'] == 'qixiaobao.dongpouu.com':
                        try:
                            pms = WxPublicMapShop.objects.get(shop_id=shop, status=WxPublicMapShop.STATUS_VALID)
                            url = request.build_absolute_uri().replace('qixiaobao.dongpouu.com',
                                                                       '%s.wx.qixiaobao.dongpouu.com' % pms.app_id)
                            if request.is_secure():
                                url = url.replace('https://', 'http://')
                            return redirect(url)
                        except WxPublicMapShop.DoesNotExist:
                            return func(request, *args, **kwargs)
                    else:
                        if request.is_secure():
                            url = request.build_absolute_uri().replace('https://', 'http://')
                            return redirect(url)

                    openid = request.session.get('show_' + shop, '')
                    timestamp_now = int(time.time())

                    ok, openid = Helper.check_cookie(openid)
                    if ok:
                        # 判断微信用户token是否存在，如果不存在，则需要授权
                        redis = RedisStat()
                        json_string = redis.get(openid)
                        if not json_string:
                            ok = False
                        else:
                            ws = json.loads(json_string)
                            if not ws['user_id']:
                                ok = False

                    if not ok:
                        # openid出错，重新授权
                        state = request.GET.get('state', None)
                        if state:
                            # aa|bb|cc  aa:最近一级上级推客 bb:谁转发过来 cc:时间戳
                            state_list = unquote(state).split('|')
                            if len(state_list) != 3:
                                state = '0|0|%d' % timestamp_now
                        else:
                            state = '0|0|%d' % timestamp_now

                        state += '|%s' % shop

                        try:
                            pms = WxPublicMapShop.objects.get(shop_id=shop, status=WxPublicMapShop.STATUS_VALID)
                        except WxPublicMapShop.DoesNotExist:
                            response = func(request, *args, **kwargs)
                        else:
                            r = RedisStat()
                            rs_id = r.get('redirect_url_id')
                            if rs_id:
                                url_id = r.incr('redirect_url_id')
                            else:
                                url_id = 1
                                r.set('redirect_url_id', url_id)
                            r.set_ttl('redirect_url_id_%d' % url_id, request.build_absolute_uri(), 60)

                            if request.is_secure():
                                domain_url = 'https://%s/open/gh/sns' % request.META['HTTP_HOST']
                            else:
                                domain_url = 'http://%s/open/gh/sns' % request.META['HTTP_HOST']
                            url = WeixinHelper.openOauth2(pms.app_id,
                                                          WxCryptConfig.app_id,
                                                          domain_url,
                                                          scope='snsapi_base',
                                                          state='base|%s|%s' % (state, url_id))  # 到这里state就有5段了：1+3+1
                            # logger.debug(url)
                            return redirect(url)
                    else:

                        # 获取绑定的User对象
                        user = authenticate(openid=openid)
                        if user is not None:
                            token, goc = Token.objects.get_or_create(user=user)
                            login(request, user)
                            response = func(request, *args, **kwargs)
                            response.set_cookie(shop + '_key', token.key, path='/')
                            response.set_cookie(shop, openid, path='/')
                        else:
                            response = func(request, *args, **kwargs)
            else:
                response = func(request, *args, **kwargs)
            return response

        return inner

    return wrap


sns_userinfo = sns_userinfo_callback()
